"""
<area>  Defines an area inside an image map
<bdo>   Defines the direction of text display
<big>   Defines big text
<blockquote>    Defines a long quotation
<button>        Defines a push button
<caption>       Defines a table caption
<cite>  Defines a citation
<dd>    Defines a definition description
<del>   Defines deleted text
<dfn>   Defines a definition term
<dl>    Defines a definition list
<dt>    Defines a definition term
<em>    Defines emphasized text
<font>  Deprecated. Defines text font, size, and color
<frame>         Defines a sub window (a frame)
<frameset>      Defines a set of frames
<iframe>        Defines an inline sub window (frame)
<legend>        Defines a title in a fieldset
<map>   Defines an image map
<noframes>      Defines a noframe section
<noscript>      Defines a noscript section
<object>        Defines an embedded object
<param>         Defines a parameter for an object
<small>         Defines small text
<strike>        Deprecated. Defines strikethrough text
<strong>        Defines strong text

Core Attributes (Not valid in base, head, html, meta, param, script, style, title)
class, id, style, title, accesskey, tabindex

Language Attributes (Not valid in base, br, frame, frameset, hr, iframe, param, script)
dir, lang

Window Events (only valid in body and frameset elements)
onload, onunload

Form Element Events (only valid in form elements)
onchange, onsubmit, onreset, onselect, onblur, onfocus

Keyboard Events (Not valid in base, bdo, br, frame, frameset, head, html, 
iframe, meta, param, script, style, title)
onkeydown, onkeypress, onkeyup
onclick, ondblclick
onmousedown, onmousemove, onmouseover, onmouseout, onmouseup
"""
from ganges.text import TextBlock 
import colors


def escape_html(text, spaces = True, breaks = True):
    out = []
    for ch in text:
        if ch == "&":
            out.append("&amp;")
        elif ch == "<":
            out.append("&lt;")
        elif ch == ">":
            out.append("&gt;")
        elif ch == '"':
            out.append("&quot;")
        elif ch == "'":
            out.append("&#39;")
        elif spaces and ch == " ":
            out.append("&nbsp;")
        elif breaks and ch == "\n":
            out.append("<br />\n")
        else:
            out.append(ch)
    return "".join(out)


class HtmlTag(object):
    INDENT_WITH = "  "
    RENDER_LEAN = False
    RENDER = False
    NAME = None
    AUTO_ID = True
    ATTRS = None
    DEFAULT_ATTRS = {}
    ELEMS = ()
    ESCAPE_TEXT = True
    FORCE_CLOSE = False
    _UID = 0
    
    def __init__(self, *_elems, **_attrs):
        self.elems = list(_elems)
        self.attrs = _attrs
        for k, v in self.DEFAULT_ATTRS.iteritems():
            if k not in self.attrs:
                self.attrs[k] = v
        if self.ATTRS is not None:
            for k, v in self.attrs.items():
                k = k.replace("_", "-")
                if k not in self.attrs:
                    self.attrs[k] = v
                if k not in self.ATTRS:
                    raise ValueError("invalid attribute %r" % (k,))
                self.attrs[k] = self.ATTRS[k](v)
        for e in self.elems:
            if not self.ELEMS or not isinstance(e, self.ELEMS):
                raise ValueError("%s does not accept %r element" % 
                    (self.__class__.__name__, e))
        self.id = self.attrs.get("id", None)
        if self.id is None and self.AUTO_ID:
            self.attrs["id"] = self.id = "_id%d" % (self._UID,)
            self.__class__._UID += 1
    
    def render(self, nesting = 0):
        if self.RENDER_LEAN:
            ind = ""
        else:
            ind = self.INDENT_WITH * nesting
        text = [ind, "<", self.NAME.lower()]
        if self.attrs:
            for k, v in self.attrs.iteritems():
                text.append(" ")
                text.append(k.lower())
                if v is not None or v is True:
                    text.append(' = "')
                    text.append(escape_html(str(v), spaces=False, breaks = False))
                    text.append('"')
        if not self.elems:
            if self.FORCE_CLOSE:
                text.append("></")
                text.append(self.NAME.lower())
                text.append(">\n")
            else:
                text.append(" />\n")
        else:
            text.append(">")
            if not self.RENDER_LEAN:
                text.append("\n")
                ind2 = self.INDENT_WITH * (nesting + 1)
            else:
                ind2 = ""
            for e in self.elems:
                if isinstance(e, HtmlTag):
                    text.append(e.render(nesting + 1))
                else:
                    for l in str(e).splitlines():
                        text.append(ind2)
                        if self.ESCAPE_TEXT:
                            text.append(escape_html(l))
                        else:
                            text.append(l)
                        if not self.RENDER_LEAN:
                            text.append("\n")
            text.append(ind)
            text.append("</")
            text.append(self.NAME.lower())
            text.append(">\n")
        return "".join(text)


#===============================================================================
# Special elements
#===============================================================================
class HeadElement(HtmlTag):
    AUTO_ID = False

class BodyElement(HtmlTag):
    pass

class ChildElement(HtmlTag):
    pass

class Comment(BodyElement):
    def __init__(self, text):
        self.text = text
    def render(self, nesting = 0):
        if not self.RENDER_LEAN:
            ind = self.INDENT_WITH * nesting
        else:
            ind = ""
        return ind + "<!-- " + self.text.replace("--", "__") + " -->\n" 

class Script(BodyElement):
    NAME = "script"
    ELEMS = (basestring,)
    ESCAPE_TEXT = False
    FORCE_CLOSE = True
    AUTO_ID = False

class Style(BodyElement):
    NAME = "style"
    ELEMS = (basestring,)
    ESCAPE_TEXT = False
    FORCE_CLOSE = True
    AUTO_ID = False

class Head(HtmlTag):
    NAME = "head"
    AUTO_ID = False
    ELEMS = (HeadElement, Script, Style, Comment)

class Body(HtmlTag):
    NAME = "body"
    AUTO_ID = False
    ELEMS = (BodyElement, Comment, basestring)

class HtmlRoot(HtmlTag):
    NAME = "html"
    AUTO_ID = False
    ELEMS = (Head, Body, Comment)

class XHtmlRoot(HtmlRoot):
    DEFAULT_ATTRS = {"xmlns" : "http://www.w3.org/1999/xhtml"}
    DOCTYPE = ('<!DOCTYPE html' +
               'PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"' +
               '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">')
    
    def render(self, nesting = 0):
        text = HtmlRoot.render(self, nesting)
        return self.DOCTYPE + "\n" + text 


#===============================================================================
# Head elements
#===============================================================================
class Title(HeadElement):
    NAME = "title"
    ELEMS = (basestring,)

class Meta(HeadElement):
    NAME = "meta"

class Link(HeadElement):
    NAME = "link"


#===============================================================================
# Body elements
#===============================================================================

class Caption(ChildElement):
    NAME = "Caption"
    ELEMS = (BodyElement,)

class Col(ChildElement):
    NAME = "col"
    ELEMS = ()

class ColGroup(ChildElement):
    NAME = "colgroup"
    ELEMS = (Col,)

class Td(ChildElement):
    NAME = "td"
    ELEMS = (BodyElement, basestring)

class Th(ChildElement):
    NAME = "th"
    ELEMS = (BodyElement, basestring)

class Tr(ChildElement):
    NAME = "tr"
    ELEMS = (Td, Th)

class THead(ChildElement):
    NAME = "thead"
    ELEMS = (Tr,)

class TBody(ChildElement):
    NAME = "tbody"
    ELEMS = (Tr,)

class TFoot(ChildElement):
    NAME = "tfoot"
    ELEMS = (Tr,)

class Table(BodyElement):
    NAME = "table"
    ELEMS = (Tr, THead, TFoot, TBody, ColGroup, Caption)

class Anchor(BodyElement):
    NAME = "a"
    ELEMS = (BodyElement, basestring)

class Para(BodyElement):
    NAME = "p"
    ELEMS = (BodyElement, basestring)

class Div(BodyElement):
    NAME = "div"
    ELEMS = (BodyElement, basestring)

class Span(BodyElement):
    NAME = "span"
    ELEMS = (BodyElement, basestring)

class Quot(BodyElement):
    NAME = "q"
    ELEMS = (BodyElement, basestring)

class H1(BodyElement):
    NAME = "h1"
    ELEMS = (BodyElement, basestring)

class H2(BodyElement):
    NAME = "h2"
    ELEMS = (BodyElement, basestring)

class H3(BodyElement):
    NAME = "h3"
    ELEMS = (BodyElement, basestring)

class H4(BodyElement):
    NAME = "h4"
    ELEMS = (BodyElement, basestring)

class H5(BodyElement):
    NAME = "h5"
    ELEMS = (BodyElement, basestring)

class H6(BodyElement):
    NAME = "h6"
    ELEMS = (BodyElement, basestring)

class Bold(BodyElement):
    NAME = "bold"
    ELEMS = (BodyElement, basestring)

class Italic(BodyElement):
    NAME = "i"
    ELEMS = (BodyElement, basestring)

class Underlined(BodyElement):
    NAME = "u"
    ELEMS = (BodyElement, basestring)

class Sub(BodyElement):
    NAME = "sub"
    ELEMS = (BodyElement, basestring)

class Sup(BodyElement):
    NAME = "sup"
    ELEMS = (BodyElement, basestring)

class Acronym(BodyElement):
    NAME = "acronym"
    ELEMS = (BodyElement, basestring,)

class Abbr(BodyElement):
    NAME = "abbr"
    ELEMS = (BodyElement, basestring)

class Label(BodyElement):
    NAME = "label"
    ELEMS = (BodyElement, basestring)

class Anchor(BodyElement):
    NAME = "a"
    ELEMS = (BodyElement, basestring)

class Image(BodyElement):
    NAME = "img"
    ELEMS = ()

class Br(BodyElement):
    NAME = "br"
    ELEMS = ()

class Hr(BodyElement):
    NAME = "br"
    ELEMS = ()

class Li(BodyElement):
    NAME = "li"
    ELEMS = (BodyElement, basestring)

class Ul(BodyElement):
    NAME = "ul"
    ELEMS = (Li,)

class Ol(BodyElement):
    NAME = "ol"
    ELEMS = (Li,)

class Pre(BodyElement):
    NAME = "pre"
    ELEMS = (basestring,)

class Code(BodyElement):
    NAME = "code"
    ELEMS = (BodyElement, basestring)

class FieldSet(BodyElement):
    NAME = "fieldset"
    ELEMS = (BodyElement, basestring)

class Form(BodyElement):
    NAME = "form"
    ELEMS = (BodyElement,)

class FormElement(BodyElement):
    pass

class Input(FormElement):
    NAME = "input"
    ELEMS = ()

class Option(ChildElement):
    NAME = "option"
    ELEMS = (BodyElement,)

class OptGroup(ChildElement):
    NAME = "optgroup"
    ELEMS = (Option,)

class Select(FormElement):
    NAME = "select"
    ELEMS = (Option, OptGroup)

class TextArea(FormElement):
    NAME = "textarea"
    ELEMS = (basestring,)


#===============================================================================
# macros
#===============================================================================
def JScript(text):
    return Script(text, language = "javascript")

def ScriptSrc(url, lang = "javascript"):
    return Script(src = url, language = lang)

def Href(text, url):
    return Anchor(text, url = url)

def Document(title, content_type = "text/html; charset=utf-8", script_urls = [], body = []):
    head = Head(
        Title("moshe"),
        Meta(http_equiv = "content-type", content = content_type),
    )
    for url in script_urls:
        head.elems.append(ScriptSrc(url))
    return XHtmlRoot(head, Body(*body))


#===============================================================================
# self test
#===============================================================================
if __name__ == "__main__":
    doc = Document("moshe", 
        body = [
            Comment("this is gay"),
            Para("he&llo"),
        ]
    )
    print doc.render()















